"""Apply Image Transformation
.. helpdoc::
This widget applys a transformation on the input image (matrix).  Many transformations are possible and advanced transformations can be input in an advanced tab (note that the developers are not responsible for malicious or erronious code placed into any advanced utility).  Users can also input a size or mask on which to transform the data.

The widget will take data of the from any widget that outputs an image type (or technically any matrix type).  Not all parameters may be supported for every transformation as a GUI interface.  However, the user is welcome to input optional parameters into the advanced GUI sections.

The following parameters can be passed to the filters:

Filters scipy.ndimage.filters
convolve(input, weights[, output, mode, ...])    Multi-dimensional convolution.
convolve1d(input, weights[, axis, output, ...])    Calculate a one-dimensional convolution along the given axis.
correlate(input, weights[, output, mode, ...])    Multi-dimensional correlation.
correlate1d(input, weights[, axis, output, ...])    Calculate a one-dimensional correlation along the given axis.
gaussian_filter(input, sigma[, order, ...])    Multi-dimensional Gaussian filter.
gaussian_filter1d(input, sigma[, axis, ...])    One-dimensional Gaussian filter.
gaussian_gradient_magnitude(input, sigma[, ...])    Calculate a multidimensional gradient magnitude using gaussian derivatives.
gaussian_laplace(input, sigma[, output, ...])    Calculate a multidimensional laplace filter using gaussian second derivatives.
generic_filter(input, function[, size, ...])    Calculates a multi-dimensional filter using the given function.
generic_filter1d(input, function, filter_size)    Calculate a one-dimensional filter along the given axis.
generic_gradient_magnitude(input, derivative)    Calculate a gradient magnitude using the provided function for the gradient.
generic_laplace(input, derivative2[, ...])    Calculate a multidimensional laplace filter using the provided second derivative function.
laplace(input[, output, mode, cval])    Calculate a multidimensional laplace filter using an estimation for the second derivative based on differences.
maximum_filter(input[, size, footprint, ...])    Calculates a multi-dimensional maximum filter.
maximum_filter1d(input, size[, axis, ...])    Calculate a one-dimensional maximum filter along the given axis.
median_filter(input[, size, footprint, ...])    Calculates a multi-dimensional median filter.
minimum_filter(input[, size, footprint, ...])    Calculates a multi-dimensional minimum filter.
minimum_filter1d(input, size[, axis, ...])    Calculate a one-dimensional minimum filter along the given axis.
percentile_filter(input, percentile[, size, ...])    Calculates a multi-dimensional percentile filter.
prewitt(input[, axis, output, mode, cval])    Calculate a Prewitt filter.
rank_filter(input, rank[, size, footprint, ...])    Calculates a multi-dimensional rank filter.
sobel(input[, axis, output, mode, cval])    Calculate a Sobel filter.
uniform_filter(input[, size, output, mode, ...])    Multi-dimensional uniform filter.
uniform_filter1d(input, size[, axis, ...])    Calculate a one-dimensional uniform filter along the given axis.

"""

"""
<widgetXML>    
    <name>Apply Image Transformation</name>
    <icon>default.png</icon>
    <tags> 
        <tag>Image Analysis</tag> 
    </tags>
    <summary>Apply an image transformation to an image.</summary>
    <author>
        <authorname>Kyle R. Covington</authorname>
        <authorcontact>kyle@red-r.org</authorcontact>
    </author>
</widgetXML>
"""
from OWRpy import * 
import redRGUI, signals 
import numpy, pylab
import scipy.ndimage as ndi

import redRi18n
_ = redRi18n.get_(package = 'base')
class applyImageTransform(OWRpy):
    def __init__(self, **kwargs):
        OWRpy.__init__(self, **kwargs)
        self.data=None
        """.. rrsignals::"""
        self.inputs.addInput('id0', _('Image'), signals.base.NumpyArray, self.processImage)

        """.. rrsignals::"""
        self.outputs.addOutput('id0', _('Image'), signals.base.NumpyArray)

        self.mainTabs = redRGUI.base.tabWidget(self.controlArea)
        self.mainFunctionSelect = self.mainTabs.createTabPage("Main Function Select", orientation = 'vertical') # specify the functions, the major tab page
        self.advacedFunctionGeneration = self.mainTabs.createTabPage("Advaced Function Generation", orientation = 'vertical') # specify advanced functions
        self.imageDisplay = self.mainTabs.createTabPage("Image Display", orientation = 'vertical') # display the image so the user can check.
        
        mainFunctionGrid = redRGUI.base.gridBox(self.mainFunctionSelect)
        self.functionListBox = redRGUI.base.listBox(mainFunctionGrid.cell(0,0), label = 'Function Select', callback = self.functionSelected, 
            items = [('convolve', 'Multi-Dimensional Convolution'), 
            ('convolve1d', 'One-Dimensional Convolution'), 
            ('correlate', 'Multi-Dimensional Correlation'), 
            ('correlate1d', 'One-Dimensional Correlation'), 
            ('gaussian_filter', 'Muli-Dimensional Gaussian Filter'), 
            ('gaussian_filter1d', 'One-Dimensional Gaussian Filter'), 
            ('gaussian_gradient_magnitude', 'Multi-Dimensional Gradient Magnitude'), 
            ('gaussian_laplace', 'Muli-Dimensional Gaussian Laplace Filter'), 
            ('laplace', 'Muli-Dimensional Laplace Filter'), 
            ('maximum_filter', 'Maximum Intensity'), 
            ('maximum_filter1d', 'Maximum Intensity One-Dimensional'), 
            ('median_filter', 'Muli-Dimensional Median Filter'), 
            ('minimum_filter', 'Muli-Dimensional Minimum Filter'), 
            ('minimum_filter1d', 'One-Dimensional Minimum Filter'), 
            ('percentile_filter', 'Muli-Dimensional Percentile Filter'), 
            ('prewitt', 'Prewitt Filter'), 
            ('rank_filter', 'Muli-Dimensional Rank Filter'), 
            ('sobel', 'Sobel Filter'),
            ('binary_opening', 'Binary Opening'),
            ('binary_closing', 'Binary Closing'),
            ('binary_dialation', 'Binary Dialation'),
            ('binary_erosion', 'Binary Erosion')]) #display the functions that can be selected and when one is selected, display the available parameters of the function.
        
        #self.GUI_mask = redRGUI.base.textEdit(self.advacedFunctionGeneration, label = "Mask", toolTip = "Please enter a mask in standard python notation.  For example [[0,1,0],[1,1,1],[0,1,0]].")
        self.GUI_kwargs = redRGUI.base.textEdit(self.advacedFunctionGeneration, label = "Key Word Arguments", toolTip = "Please enter any key word arguments in standard python notation.  This is required by some functions.")
        
        self.GUI_changeOnFunctionSelectCheckbox = redRGUI.base.TFCheckBox(mainFunctionGrid.cell(1,0), label = 'Commit On Function Select', setChecked = True)
        self.GUI_showImage = redRGUI.base.TFCheckBox(self.controlArea, label = "Show Image", setChecked = False)
        self.GUI_plot = redRGUI.RedRMatplotlib.mplPlot(self.controlArea)
        
        settingsArea = redRGUI.base.widgetBox(mainFunctionGrid.cell(0,1), orientation = 'vertical')
        self.GUI_size = redRGUI.base.spinBox(settingsArea, label = 'Filter Size', decimal = 0, min = 1)
        self.GUI_shape = redRGUI.base.radioButtons(settingsArea, label = "Shape:", buttons = [(0, "Square"), (1, "Circle")])
        self.GUI_weights = redRGUI.base.spinBox(settingsArea, label = "Weights", decimal = 0, min = 1)
        self.GUI_sigma = redRGUI.base.spinBox(settingsArea, label = "Sigma")
        self.GUI_percentile = redRGUI.base.spinBox(settingsArea, label = "Percentile", min = 0, max = 100, decimal = 0)
        
        self.commitButton = redRGUI.base.commitButton(self.bottomAreaRight, label = "Commit", callback = self.commitFunction, processOnInput = True)

    def processImage(self, data):
        if data:
            self.data=data.getData()
            self.GUI_plot.subPlot.clear()
            if self.commitButton.processOnInput():
                self.commitFunction()
        else:
            self.data=None
    def functionSelected(self):
        if self.GUI_changeOnFunctionSelectCheckbox.checked() == 'TRUE':
            self.commitFunction()
    def commitFunction(self):
        if self.data == None:
            self.status.setText("No Data To Apply Filtering To")
            return
        function = self.functionListBox.selectedIds()
        if function == None or len(function) > 1: return
        function = function[0]
        kwargs = eval("{%s}" % self.GUI_kwargs.toPlainText())
        try:
            if function == 'convolve':
                newImage = ndi.filters.convolve(self.data, self.GUI_weights.value(), **kwargs)
            elif function == 'convolve1d':
                newImage = ndi.filters.convolve1d(self.data, self.GUI_weights.value(), **kwargs)
            elif function == 'correlate':
                newImage = ndi.filters.correlate(self.data, self.GUI_weights.value(), **kwargs)
            elif function == 'correlate1d':
                newImage = ndi.filters.correlate1d(self.data, self.GUI_weights.value(), **kwargs)
            elif function == 'gaussian_filter':
                newImage = ndi.filters.gaussian_filter(self.data, self.GUI_sigma.value(), **kwargs)
            elif function == 'gaussian_filter1d':
                newImage = ndi.filters.gaussian_filter1d(self.data, self.GUI_sigma.value(), **kwargs)
            elif function == 'gaussian_gradient_magnitude':
                newImage = ndi.filters.gaussian_gradient_magnitude(self.data, self.GUI_sigma.value(), **kwargs)
            elif function == 'gaussian_laplace':
                newImage = ndi.filters.gaussian_laplace(self.data, self.GUI_sigma.value(), **kwargs)
            elif function == 'laplace':
                newImage = ndi.filters.laplace(self.data)
            elif function == 'maximum_filter' and self.GUI_shape.getCheckedId() == 0:
                newImage = ndi.filters.maximum_filter(self.data, size = self.GUI_size.value(), **kwargs)
            elif function == 'maximum_filter1d' and self.GUI_shape.getCheckedId() == 0:
                newImage = ndi.filters.maximum_filter1d(self.data, size = self.GUI_size.value(), **kwargs)
            elif function == 'median_filter' and self.GUI_shape.getCheckedId() == 0:
                newImage = ndi.filters.median_filter(self.data, size = self.GUI_size.value(), **kwargs)
            elif function == 'minimum_filter' and self.GUI_shape.getCheckedId() == 0:
                newImage = ndi.filters.minimum_filter(self.data, size = self.GUI_size.value(), **kwargs)
            elif function == 'minimum_filter1d' and self.GUI_shape.getCheckedId() == 0:
                newImage = ndi.filters.minimum_filter1d(self.data, size = self.GUI_size.value(), **kwargs)
            elif function == 'percentile_filter' and self.GUI_shape.getCheckedId() == 0:
                newImage = ndi.filters.percentile_filter(self.data, size = self.GUI_size.value(), percentile = float(self.GUI_percentile.value())/100.0, **kwargs)
            elif function == 'maximum_filter' and self.GUI_shape.getCheckedId() == 1:
                newImage = ndi.filters.maximum_filter(self.data, footprint = self.makeCircle(self.GUI_size.value()), **kwargs)
            elif function == 'maximum_filter1d' and self.GUI_shape.getCheckedId() == 1:
                newImage = ndi.filters.maximum_filter1d(self.data, footprint = self.makeCircle(self.GUI_size.value()), **kwargs)
            elif function == 'median_filter' and self.GUI_shape.getCheckedId() == 1:
                newImage = ndi.filters.median_filter(self.data, footprint = self.makeCircle(self.GUI_size.value()), **kwargs)
            elif function == 'minimum_filter' and self.GUI_shape.getCheckedId() == 1:
                newImage = ndi.filters.minimum_filter(self.data, footprint = self.makeCircle(self.GUI_size.value()), **kwargs)
            elif function == 'minimum_filter1d' and self.GUI_shape.getCheckedId() == 1:
                newImage = ndi.filters.minimum_filter1d(self.data, footprint = self.makeCircle(self.GUI_size.value()), **kwargs)
            elif function == 'percentile_filter' and self.GUI_shape.getCheckedId() == 1:
                newImage = ndi.filters.percentile_filter(self.data, footprint = self.makeCircle(self.GUI_size.value()), percentile = float(self.GUI_percentile.value())/100.0, **kwargs)
            elif function == 'rank_filter' and self.GUI_shape.getCheckedId() == 0:
                newImage = ndi.filters.rank_filter(self.data, rank = self.GUI_rank.value(), size = self.GUI_size.value(), **kwargs)
            elif function == 'rank_filter' and self.GUI_shape.getCheckedId() == 1:
                newImage = ndi.filters.rank_filter(self.data, rank = self.GUI_rank.value(), footprint = self.makeCircle(self.GUI_size.value()), **kwargs)
            elif function == 'prewitt':
                newImage = ndi.filters.prewitt(self.data, **kwargs)
            elif function == 'sobel':
                newImage = ndi.filters.sobel(self.data, **kwargs)
            elif function == 'binary_closing':
                newImage = ndi.binary_closing(self.data.astype(numpy.int), structure = self.makeCircle(self.GUI_size.value()), **kwargs)
            elif function == 'binary_dialation':
                newImage = ndi.binary_dialation(self.data.astype(numpy.int), structure = self.makeCircle(self.GUI_size.value()), **kwargs)
            elif function == 'binary_erosion':
                newImage = ndi.binary_erosion(self.data.astype(numpy.int), structure = self.makeCircle(self.GUI_size.value()), **kwargs)
            elif function == 'binary_opening':
                newImage = ndi.binary_opening(self.data.astype(numpy.int), structure = self.makeCircle(self.GUI_size.value()), **kwargs)
        except Exception as inst:
            self.status.setText('Error occured: %s' % inst)
            return
            
        newData = signals.base.NumpyArray(self, data = newImage)
        self.rSend('id0', newData)
        
        if self.GUI_showImage.checked() == "TRUE":
            self.GUI_plot.subPlot.imshow(newImage)
            self.GUI_plot.canvas.draw()
        
        
    def makeCircle(self, size):
        """Make a numpy array based on the size that is approximately circular"""
        a = numpy.zeros( (int(size*2+1), int(size*2+1)) )
        for i in range(int(size*2+1)):
            for j in range(int(size*2+1)):
                if (i - size)**2 + (j - size) **2 < size**2:
                    a[i,j] = 1
        return a
        
